"""
配置表读取器
"""

import os
import abc
import csv
import weakref
from collections import namedtuple
from typing import List, Dict,  Any, Optional, override
from .export_strategy import ExportStrategy

CfgKeyValuePair = namedtuple("CfgKeyValuePair", ["fieldName", "value"])

class CfgItem(object):
	def __init__(self) -> None:
		self.m_FieldNameList = []  # 为了保留字段的先后顺序设置的列表
		self.m_FieldDict: Dict[str, Any] = {}
		self.m_WrCheckCB = None

	def __setitem__(self, key: Any, value: Any) -> None:
		if key is None:
			print("Field's name can't be None.")
			return

		if self.m_WrCheckCB:
			oCheckCBFunc = self.m_WrCheckCB()
			if oCheckCBFunc:
				oCheckCBFunc(self)

		self.m_FieldDict[key] = value
		self.m_FieldNameList.append(key)

	def __iter__(self):
		for fieldName in self.m_FieldNameList:
			yield CfgKeyValuePair(fieldName, self.m_FieldDict[fieldName])

	def __getitem__(self, key: Any) -> Any:
		if isinstance(key, str):  # 通过字段名访问
			return self.m_FieldDict.get(key, None)
		elif isinstance(key, int):  # 通过字段所在的列索引访问
			return self.m_FieldDict.get(self.m_FieldNameList[key], None)
		return None

	def __str__(self) -> str:
		if not self.m_FieldDict or not self.m_FieldNameList:
			return "{ }"

		lstFieldKWStr = []
		for key in self.m_FieldNameList:
			value = self.m_FieldDict[key]
			filedKWStr = "\t\"{}\": \"{}\"".format(key, value)
			lstFieldKWStr.append(filedKWStr)

		resFieldJoinStr = ",\n".join(lstFieldKWStr)
		return "{{\n {} \n}}\n".format(resFieldJoinStr)


class CfgReader(metaclass=abc.ABCMeta):
	def __init__(self, cfgPath: str, exportStrategy: ExportStrategy) -> None:
		self.m_CfgFile = None
		self.m_CfgFilePath = cfgPath
		self.m_ExportStrategy = exportStrategy

		self.m_FiledDescNameList: List = []
		self.m_FiledNameList: List = []
		self.m_FiledTypeNameList: List = []

		self.m_CfgItemList: List[CfgItem] = []

		self.m_UniqueMemoryDict: Dict[str, Dict[Any, int]] = {}  # {fieldName: {value: itemIdx}}

		self._LoadFile(cfgPath)
		self._LoadCfgItems()

	def _CheckCB(self):
		pass

	@abc.abstractclassmethod
	def _LoadFile(self, cfgPath: str) -> bool:
		return False

	@abc.abstractclassmethod
	def _LoadCfgItems(self) -> None:
		return None

	def SetUnique(self, fieldName: str, value: Any, index: int) -> bool:
		if value is None:
			return True

		if fieldName in self.m_UniqueMemoryDict:
			if value in self.m_UniqueMemoryDict[fieldName]:
				print(f"\"{self.m_CfgFilePath}\": filed \"{fieldName}\"'s value \"{value}\" can't repeat set.")
				return False  # value之前已设置过, 当前在尝试重复设置, 异常情况
			self.m_UniqueMemoryDict[fieldName][value] = index
		else:
			self.m_UniqueMemoryDict[fieldName] = {value: index}

		return True

	def Export(self, exportPath: str, fileName: str) -> bool:
		if not os.path.exists(exportPath):
			os.makedirs(exportPath)

		exportFilePath = os.path.join(exportPath, fileName)

		exportStr = self.m_ExportStrategy.Export(self)
		with open(exportFilePath, mode="w", encoding="utf-8") as file:
			file.write(exportStr)


	def GetTypeNameWithIdx(self, index):
		return self.m_FiledTypeNameList[index]

	def __getitem__(self, index) -> Optional[CfgItem]:
		"""返回索引为index的CfgItem"""
		return self.m_CfgItemList[index]

	def __str__(self) -> None:
		showStr = ""
		for oItem in self.m_CfgItemList:
			showStr += str(oItem) + "\n"

		return showStr

class CfgCSVReader(CfgReader):
	def __init__(self, cfgPath: str, exportStrategy: ExportStrategy) -> None:
		CfgReader.__init__(self, cfgPath, exportStrategy)
		
		self.m_CSVReader = None

	@override
	def _LoadFile(self, cfgPath: str) -> bool:
		if not os.path.exists(cfgPath):
			print(f"Can't found config file \"{cfgPath}\"")
			return False

		if not os.path.isfile(cfgPath):
			print(f"\"{cfgPath}\" are not file, cfgPath must be a file")
			return False

		try:
			self.m_CfgFile = open(cfgPath, mode="r", newline="", encoding="utf-8")
		except OSError:
			print(f"Load \"{cfgPath}\" fail.")
			return False
		
		self.m_CSVReader = csv.reader(self.m_CfgFile, delimiter=",", quotechar="|")
		return True

	@override
	def _LoadCfgItems(self) -> None:
		# 读取表头
		self.m_FiledDescNameList = next(self.m_CSVReader)
		self.m_FiledNameList = next(self.m_CSVReader)
		self.m_FiledTypeNameList = next(self.m_CSVReader)

		if not len(self.m_FiledDescNameList) == len(self.m_FiledNameList) == len(self.m_FiledTypeNameList):
			print("Config file headers must be same length")
			return

		# 读取配置项
		for row in self.m_CSVReader:
			oItem = CfgItem()
			for itemIdx, value in enumerate(row):
				oItem[self.m_FiledNameList[itemIdx]] = value

			self.m_CfgItemList.append(oItem)

	def __getitem__(self, index) -> CfgItem:
		return self.m_CfgItemList[index]

	